// 在测试时要在编译末尾加[-pthread]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
// 全局数据类型定义
#define ElemType int
#define ElemTypes char
#define Internet_Protocol ("192.168.20.128")
// 全局变量
// 信息储存区
ElemTypes storage_area[1024] = {0};
ElemType detection = 1;
// 函数库
// 全境广播
void Broadcast_throughout_the_world();
// 广播调度系统->客户端
void point_to_point_communication();
ElemType main()
{
    system("clear");
    // 创建UDP通信通道socket
    ElemType UDP_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == UDP_socket_fd)
    {
        perror("socket failed\n");
        return -1;
    }
    // 设置发送方IP地址
    struct sockaddr_in send_addr;
    // 初始化服务器地址
    bzero(&send_addr, sizeof(send_addr));
    // IPV4协议
    send_addr.sin_family = AF_INET;
    // 设置发送方端口号
    send_addr.sin_port = htons(8848);
    // 系统检测本地网卡， 自动绑定本地IP
    send_addr.sin_addr.s_addr = inet_addr(Internet_Protocol);
    ElemType ret = bind(UDP_socket_fd, (struct sockaddr *)&send_addr, sizeof(send_addr));
    if (ret < 0)
    {
        perror("bind fail:");
        close(UDP_socket_fd);
        return -1;
    }
    else
        printf("连接准备就绪\n");
    // 储存信息发送方的IP地址
    struct sockaddr_in src_addr;
    // 初始化
    bzero(&src_addr, sizeof(src_addr));
    // 地址信息大小
    ElemType len = sizeof(src_addr);
    // 信息缓冲区
    ElemTypes buff[1024] = {0};
    // 循环接收客户端发送过来的数据
    while (1)
    {
        Broadcast_throughout_the_world();
        ret = recvfrom(UDP_socket_fd, buff, sizeof(buff), 0, (struct sockaddr *)&src_addr, &len);
        if (ret == -1)
            break;
        // 打印消息发送方的IP与端口号
        printf("[%s,:%d]", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));
        printf("信息%s\n", buff);
        if (strcmp(buff, "exit") == 0)
            break;
    }
    // 关闭通信通道
    close(UDP_socket_fd);
    return 0;
}
// 全境广播
void Broadcast_throughout_the_world()
{
    // 创建UDP套接字socket
    ElemType sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
        perror("socket error");
    // 准备广播地址和端口
    struct sockaddr_in servaddr;
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(10086);
    servaddr.sin_addr.s_addr = INADDR_BROADCAST;
    // 设置广播套接字选项 SO_BROADCAST
    ElemType so_broadcast = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &so_broadcast, sizeof(so_broadcast)) < 0)
    {
        perror("setsockopt");
        close(sockfd);
        return;
    }
    // 使用 sendto 发送广播数据报
    if (sendto(sockfd, (""), strlen("已完成A通信通道的建立"), 0, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
        perror("发送错误原因");
    // 处理应答
    ElemTypes recvline[256];
    ElemType n = 0;
    struct sockaddr_in tmpAddr;
    bzero(&tmpAddr, sizeof(tmpAddr));
    socklen_t addrLen = sizeof(tmpAddr);
    while ((n = recvfrom(sockfd, recvline, sizeof(recvline), 0, (struct sockaddr *)&tmpAddr, &addrLen)) > 0)
    {
        recvline[n] = 0;
        detection = 0;
        // 清空接收到的信息， 避免影响下次接收
        bzero(storage_area, sizeof(storage_area));
        printf("接收IP=[%s], [%s]\n", inet_ntoa(tmpAddr.sin_addr), recvline);
        bzero(&tmpAddr, sizeof(tmpAddr));
        // 广播调度系统->客户端
        point_to_point_communication();
        if (sendto(sockfd, storage_area, strlen("已完成B通信通道的建立"), 0, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
            perror("发送错误原因");
        // 清空接收到的信息， 避免影响下次接收
        bzero(storage_area, sizeof(storage_area));
    }
    if (n < 0)
        perror("read error");
    // 关闭
    close(sockfd);
    return;
}
// 广播调度系统->客户端
void point_to_point_communication()
{
    // 连接方端口号
    ElemType port = 8888;
    // 创建UDP通信通道socket
    ElemType UDP_socket_FD = socket(AF_INET, SOCK_DGRAM, 0);
    if (UDP_socket_FD < 0)
    {
        perror("creat socket fail\n");
        return;
    }
    // 加入传播组  地址区间224.0.0.0~239.255.255.255
    struct ip_mreq ZU;
    // 初始化
    bzero(&ZU, sizeof(ZU));
    // 设置组播地址
    ZU.imr_multiaddr.s_addr = inet_addr("230.52.52.52");
    ZU.imr_interface.s_addr = inet_addr("0.0.0.0");
    ElemType Ret = setsockopt(UDP_socket_FD, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ZU, sizeof(ZU));
    if (Ret < 0)
    {
        perror("setsockopt fail\n");
        return;
    }
    else
        printf("已入组：[%d], 等待信息\n", port);
    // 设置UDP地址并绑定
    struct sockaddr_in local_addr;
    // 将地址信息结构体初始化
    bzero(&local_addr, sizeof(local_addr));
    // IPV4协议
    local_addr.sin_family = AF_INET;
    // 网络通信为大端格式
    local_addr.sin_port = htons(port);
    // 32位整数
    local_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    // Linux下加入组播后， 绑定地址只能绑定0.0.0.0地址否则不会接收数据
    Ret = bind(UDP_socket_FD, (struct sockaddr *)&local_addr, sizeof(local_addr));
    if (Ret < 0)
    {
        perror("bind fail\n");
        return;
    }
    // 定义一个缓冲区存放发送方IP地址
    struct sockaddr_in src_addr;
    // 初始化
    bzero(&src_addr, sizeof(src_addr));
    ElemType len = sizeof(src_addr);
    // 信息缓冲区
    ElemTypes Buff[1024] = {0};
    ElemTypes BuffA[1024] = {0};
    ElemTypes BuffB[1024] = {0};
    // 清空接收到的信息， 避免影响下次接收
    bzero(storage_area, sizeof(storage_area));
    // 接收信息
    while (1)
    {
        recvfrom(UDP_socket_FD, Buff, sizeof(Buff), 0, (struct sockaddr *)&src_addr, &len);
        // 打印信息发送方IP地址
        ElemType BUFF = 0;
        // 当BuffB内没有数据时
        BUFF = strlen(BuffB);
        printf("[IP——%s, 发送方%d]", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));
        printf("缓冲区信息：%s\n", Buff);
        // 将BuffA值给BuffB
        memcpy(BuffB, BuffA, sizeof(BuffA));
        // 清空接收到的信息， 避免影响下次接收
        bzero(BuffA, sizeof(BuffA));
        // 将Buff值给BuffA
        memcpy(BuffA, Buff, sizeof(Buff));
        if (strcmp(Buff, "exit") == 0)
        {
            // 当接收到"exit"时退出循环
            // 将buff中的信息给广播系统
            memcpy(storage_area, BuffB, strlen(BuffB));

            // 打开文件
            if (NULL == fopen("/home/w/code/作业/威慑/广播系统/用户->广播调度系统->全境广播/信息记录.txt", "r")){
                printf("文件不存在，创建文件\n");
                FILE *fp;
                if (NULL == (fp = fopen("/home/w/code/作业/威慑/广播系统/用户->广播调度系统->全境广播/信息记录.txt", "w+"))){
                    printf("\n创建文件失败, 存入终止\n");
                    return;
                }else{
                    printf("\n创建文件成功, 存入开始\n");
                }// 写入数据
                fprintf(fp, "%s %d %s\n", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port), BuffB);
                printf("\n存档成功\n");
                // 关闭文档
                fclose(fp);
            }else{
                printf("\n文件存在, 存入开始\n");
                // 读取文档
                FILE *fp;
                if (NULL == (fp = fopen("/home/w/code/作业/威慑/广播系统/用户->广播调度系统->全境广播/信息记录.txt", "a+"))){
                    printf("\n读取文件失败, 存入终止\n");
                    return;
                }else{
                    printf("\n读取文件成功, 存入开始\n");
                }
                // 写入数据
                fprintf(fp, "%s %d %s\n", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port), BuffB);
                printf("\n存档成功\n");
                // 关闭文档
                fclose(fp);
            }
            // 清空接收到的信息， 避免影响下次接收
            bzero(BuffB, sizeof(BuffB));
            // 清空接收到的信息， 避免影响下次接收
            bzero(BuffA, sizeof(BuffA));
            // 清空接收到的信息， 避免影响下次接收
            bzero(Buff, sizeof(Buff));
            break;
        }
        // 清空接收到的信息， 避免影响下次接收
        bzero(Buff, sizeof(Buff));
    }
    // 关闭
    close(UDP_socket_FD);
}
// // 将数据存入/home/w/code/作业/威慑/广播系统/用户->广播调度系统->全境广播/信息记录.txt中, IP地址， 端口号， 信息
// void Save(ElemTypes *inet_ntoa(src_addr.sin_addr), ElemTypes *BuffB, ElemType ntohs(src_addr.sin_port))
// {
//     // 打开文件
//     if (NULL == fopen("/home/w/code/作业/威慑/广播系统/用户->广播调度系统->全境广播/信息记录.txt", "r"))
//     {
//         printf("文件不存在，创建文件\n");
//         FILE *fp;
//         if (NULL == (fp = fopen("/home/w/code/作业/威慑/广播系统/用户->广播调度系统->全境广播/信息记录.txt", "w+")))
//         {
//             printf("\n创建文件失败, 存入终止\n");
//             return;
//         }
//         else
//         {
//             printf("\n创建文件成功, 存入开始\n");
//         }
//         // 写入数据
//         fprintf(fp, "%s %d %s\n", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port), BuffB);
//         printf("\n存档成功\n");
//         // 关闭文档
//         fclose(fp);
//     }
//     else
//     {
//         printf("\n文件存在, 存入开始\n");
//         // 读取文档
//         FILE *fp;
//         if (NULL == (fp = fopen("/home/w/code/作业/威慑/广播系统/用户->广播调度系统->全境广播/信息记录.txt", "a+")))
//         {
//             printf("\n读取文件失败, 存入终止\n");
//             return;
//         }
//         else
//         {
//             printf("\n读取文件成功, 存入开始\n");
//         }
//         // 写入数据
//         fprintf(fp, "%s %d %s\n", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port), BuffB);
//         printf("\n存档成功\n");
//         // 关闭文档
//         fclose(fp);
//     }
//     return;
// }